package ai2;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;

public class KnowledgeBase {

	// a list of all the rules
	private ArrayList<Rule> rules = new ArrayList<Rule>();
	// the current rule under construction
	private Rule ruleUnderConstruction = null;
	// keeps track of assertionMode
	private boolean assertionMode = false;
	
	// create a blank KnowledgeBase
	public KnowledgeBase() {
		System.out.println("KB ** created");
	}
	
	// create a KnowledgeBase and populate it from a file
	public KnowledgeBase(String fileName) {
		System.out.println("KB ** created");
		System.out.println("KB ** rules filename: " + fileName + "\n");
		importRulesFromFile(fileName);
	}
	
	// return the list of rules
	public ArrayList<Rule> getRules() {
		System.out.println("KB -- returning rule list");
		return rules;
	}
	// print all the rules
	public void printRules() {
		System.out.println("KB -- printing rules");
		for (int i = 0; i < rules.size(); i++) {
			int ruleNumber = i + 1;
			System.out.println("KB -- rule #" + ruleNumber);
			
			ArrayList<String> antecedents = rules.get(i).getAntecedents();
			System.out.println("KB -- antecedents:");
			for (int j = 0; j < antecedents.size(); j++) {
				System.out.println("\t" + antecedents.get(j));
			}
			
			System.out.println("");
			
			ArrayList<String> assertions = rules.get(i).getAssertions();
			System.out.println("KB -- assertions:");
			for (int j = 0; j < assertions.size(); j++) {
				System.out.println("\t" + assertions.get(j));
			}
			
			System.out.println("");
		}
	}
	// external method for loading rules from a text file
	public void loadRules(String fileName) {
		clearRules();
		System.out.println("KB -- loading rules: " + fileName);
		importRulesFromFile(fileName);
	}
	// clear all rules in KB
	public void clearRules() {
		System.out.println("KB -- all rules cleared" + "\n");
		rules.clear();
	}
	// internal method for loading the rule set from a text file
	private void importRulesFromFile(String fileName) {
		File file = new File(fileName);
		// try opening file
		try {
			// load file contents into scanner
			Scanner scanner = new Scanner(file);
			scanner.useDelimiter("\n");
			
			// process each line one by one
			while (scanner.hasNext()) {
				String nextLine = scanner.next();
				// System.out.println("KB -- processing line: " + nextLine);
				// replace multiple tabs in a row with just one tab
				String cleanedUpTabs = nextLine.replaceAll("\\t+", "\t");
				// System.out.println("KB -- cleaned up tabs: " + cleanedUpTabs);
				processLineFromFile(cleanedUpTabs);
			}
			// if after the file is done being processed
			// there is a ruleUnderConstruction, add it.
			if (ruleUnderConstruction != null) {
				rules.add(ruleUnderConstruction);
				System.out.println("KB -- added final ruleUnderConstruction");	
			}
			System.out.println("KB -- done reading file." + "\n");
		}
		// file open failed
		catch (FileNotFoundException e) {
			System.out.println("KB -- file not found: " + fileName);
		}
	}
	// internal method to process a single line from a file
	private void processLineFromFile(String line) {
		// if line is empty, skip it
		if (line.equals("")) {
			// System.out.println("KB -- encountered blank line" + "\n");
			// this is a blank line, nothing happens here
		}
		// if the line starts with a # then it is a comment
		else if (line.subSequence(0, 1).equals("#")) {
			// System.out.println("KB -- encountered comment" + "\n");
			// this is a comment, nothing happens here
		}
		else {
			Scanner scanner = new Scanner(line);
			scanner.useDelimiter("\t");
			// read the line, tab delimited
			while (scanner.hasNext()) {
				String token = scanner.next();
				System.out.println("KB -- token: " + token);
				// if the line starts with an if, process as an antecedent
				if (token.equals("if")) {
					// check and see if we are in assertionMode
					if (assertionMode) {
						// we are, this means this is the start of a new rule
						// add the ruleUnderConstruction to the list of rules
						rules.add(ruleUnderConstruction);
						System.out.println("KB -- added ruleUnderConstruction to list of rules");
						// we are now out of assertionMode
						assertionMode = false;
						// set ruleUnderConstruction to null
						ruleUnderConstruction = null;
						System.out.println("KB -- exiting assertion mode" + "\n");
					}	
					// we are in antecedent mode
					System.out.println("KB -- in antecedent mode");
					// collect the antecedent
					String antecedent = scanner.next();
					System.out.println("KB -- antecedent: " + antecedent);
					// check to see if there is a ruleUnderConstruction
					if (ruleUnderConstruction == null) {
						System.out.println("KB -- creating new rule");
						// there is not, so make one
						ruleUnderConstruction = new Rule(antecedent);
					}
					else {
						System.out.println("KB -- adding antecedent to existing ruleUnderConstruction");
						// there is a ruleUnderConstruction, add to it
						ruleUnderConstruction.addNewAntecedent(antecedent);
					}
				}
				
				// if the line starts with a then, process as an assertion
				else if (token.equals("then")) {
					// make sure assertionMode is on
					assertionMode = true;
					System.out.println("KB -- in assertion mode");
					// collect the assertion
					String assertion = scanner.next();
					System.out.println("KB -- assertion: " + assertion);
					// make sure there is a ruleUnderConstruction
					if (ruleUnderConstruction == null) {
						// there should already be a ruleUnderConstruction, report error
						System.out.println("KB -- error, trying to add assertion to empty rule");
					}
					else {
						System.out.println("KB -- adding assertion to existing ruleUnderConstruction");
						// there is a ruleUnderConstruction, add to it
						ruleUnderConstruction.addNewAssertion(assertion);
					}
				}
				
				// if the token is not if or then, it is either a continuation
				// of an antecedent or an assertion
				else {
					// if we are not in assertionMode, add the token as an antecedent
					if (!assertionMode) {
						System.out.println("KB -- continuation of an antecedent");
						if (ruleUnderConstruction == null) {
							// there should already be a ruleUnderConstruction
							System.out.println("KB -- error, trying to add antecedent to empty rule");
						}
						else {
							// there is a ruleUnderConstruction, add the antecedent to it
							System.out.println("KB -- antecedent cont: " + token);
							ruleUnderConstruction.addNewAntecedent(token);
						}
					}
					// otherwise we are in assertion mode, add the token as an assertion
					else {
						System.out.println("KB -- continuation of an assertion");
						if (ruleUnderConstruction == null) {
							// there should already be a ruleUnderConstruction
							System.out.println("KB -- error, trying to add assertion to empty rule");
						}
						else {
							// there is a ruleUnderConstruction, add the assertion to it
							System.out.println("KB -- assertion cont: " + token);
							ruleUnderConstruction.addNewAssertion(token);
						}
					}
				}
			}
		}
	}
}